package com.amkai.aes.webservice.binary.objecthandler.inventory;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.amkai.aes.aescommon.service.binary.job.inventory.DictionaryItemSearchParameters;
import com.amkai.aes.aescommon.service.binary.job.inventory.OrderByItem;
import com.amkai.aes.generatedbeans.organization.Department;

public class DictionaryQueryBuilder {
	private static final Log logger = LogFactory.getLog(DictionaryQueryBuilder.class);
	
	private DictionaryItemSearchParameters params;
	private Department department;
	
	private List<Object> paramValues = new ArrayList<Object>();
	private HQLWhereNode whereNode = null;
	private List<String> fetchFields = new ArrayList<String>();
	private int instanceCount;

	// KKORNEL: AES-4465
	/*private static final String IN_DEPARTMENT_SUBQUERY = "exists (" +
			"from ItemAndDepartmentAssociation iada " +
			"where " +
			"iada.item = z " +
			"and iada.baseOrganization.id = ?" +
			")";*/

	public DictionaryQueryBuilder(DictionaryItemSearchParameters searchParams,
			Department moduleDepartment) {
		this.params = searchParams;
		this.department = moduleDepartment;
	}

	public String getCountQuery() {
		buildQueryStructure();
		StringBuilder query = initializeQuery("select count(*) ");
		addWhereConditions(query);
		return query.toString();
	}

	public String getDataQuery() {
		buildQueryStructure();
		StringBuilder query = initializeQuery(null);
		addFetchStatements(query);
		addWhereConditions(query);
		addOrderByStatements(query);
		
		return query.toString();
	}
	
	public int getQueryParameterCount() {
		return paramValues.size();
	}
	
	public Object getQueryParameterAt(int index) {
		return paramValues.get(index);
	}
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder(DictionaryQueryBuilder.class.getName());
		sb.append("(query:'");
		sb.append(getDataQuery());
		sb.append("',paramValues:[");
		boolean first = true;
		for (Object value : paramValues) {
			if (!first) {
				sb.append(",");
			}
			sb.append(value);
			first = false;
		}
		sb.append("])");
		return sb.toString();
	}

	private StringBuilder initializeQuery(String preamble) {
		StringBuilder query = new StringBuilder();
		if (preamble != null) {
			query.append(preamble);
		}
		query.append("from ");
		query.append(getObjectName());
		query.append(" as z");
		return query;
	}

	private void addWhereConditions(StringBuilder query) {
		if (whereNode == null || whereNode.isEmpty()) return;
		
		query.append(" where ");
		whereNode.walk(query, paramValues);
	}

	private void addOrderByStatements(StringBuilder query) {
		if (params.getOrderByItems() == null
				|| params.getOrderByItems().isEmpty()) {
			return;
		}
		
		query.append(" order by ");
		boolean first = true;
		for (OrderByItem orderByItem : params.getOrderByItems()) {
			if (!first) {
				query.append(", ");
			}
			first = false;
			query.append(orderByItem.toString("z"));
		}
	}

	private void addFetchStatements(StringBuilder query) {
		for (String fetchItem : fetchFields) {
			query.append(" ");
			query.append(fetchItem);
		}
	}
	
	private void buildQueryStructure() {
		whereNode = HQLWhereNodeFactory.createAndNode();
		paramValues.clear();
		fetchFields.clear();
		instanceCount = 0;
		
		for (String propertyName : params.getColumnProperties()) {
			String[] propPathItems = propertyName.split("\\.");
			if (propPathItems.length <= 1) continue;
			extractFieldsToFetch(propPathItems);
		}
		
		buildWhereDepartment();
		
		buildWhereSearchText();
		
		buildWhereTypeSelect();
	}

	private void extractFieldsToFetch(String[] propPathItems) {
		String instance = "z";
		for (int i=0; i<propPathItems.length-1; i++) {
			String nextInstance = generateInstanceName(propPathItems[i]);
			String formatStr = "left join fetch %s.%s" + (i < propPathItems.length-2 ? " %s" : "");
			fetchFields.add(String.format(formatStr, instance, propPathItems[i], nextInstance));
			instance = nextInstance;
		}
	}
	
	private String generateInstanceName(String propertyName) {
		return String.format("%s_%02d", propertyName, instanceCount++);
	}
	
	private void buildWhereTypeSelect() {
		if (!hasFilter()) return;

//		PropertyHQLGenerator generator = PropertyHQLGeneratorFactory
//				.getGenerator(params.getItemKind(),
//						params.getFilterPropertyName(),
//						true,
//						true);
//		if (generator == null) {
//			logger.severe(String.format("Could not retrieve a PropertyHQLGenerator " +
//					"for the item kind: %s and the filter property: %s",
//					params.getItemKind(),
//					params.getFilterPropertyName()));
//			return;
//		}
//		
//		whereNode.addChild(HQLWhereNodeFactory.createParametrizedConditionNode(
//				generator.getHQLFragment("z", params.getFilterPropertyName()),
//				generator.getValueFromText(params.getFilterPropertyValue())));
	}

	private void buildWhereSearchText() {
		if (!hasSearchText()) return;
		
		HQLWhereNode orNode = HQLWhereNodeFactory.createOrNode();
		whereNode.addChild(orNode);
		for (String propertyName : params.getSearchProperties()) {
//			PropertyHQLGenerator generator = PropertyHQLGeneratorFactory
//					.getGenerator(params.getItemKind(), propertyName, false,
//							false);
//			orNode.addChild(HQLWhereNodeFactory
//					.createParametrizedConditionNode(generator
//							.getHQLFragment("z", propertyName), generator
//							.getValueFromText(params.getSearchText())));
		}
	}

	private void buildWhereDepartment() {
		switch (params.getQueryActive()) {
		// KKORNEL: AES-4465
		case ACTIVE:
			whereNode.addChild(HQLWhereNodeFactory
					.createSimpleConditionNode("z.active=1"));
					/*.createParametrizedConditionNode(IN_DEPARTMENT_SUBQUERY,
							department.getId()));*/
			break;
		case INACTIVE:
			whereNode.addChild(HQLWhereNodeFactory
					.createSimpleConditionNode("z.active=0"));
					/*.createParametrizedConditionNode("not "
							+ IN_DEPARTMENT_SUBQUERY, department.getId()));*/
			break;
		default:
			// do nothing
		}
	}
	
	private boolean hasSearchText() {
		return (params.getSearchText() != null
				&& !params.getSearchText().isEmpty()
				&& params.getSearchProperties() != null
				&& params.getSearchProperties().length > 0);
	}
	
	private boolean hasFilter() {
		return (params.getFilterPropertyName() != null
				&& !params.getFilterPropertyName().isEmpty()
				&& params.getFilterPropertyValue() != null
				&& !params.getFilterPropertyValue().isEmpty()
				&& params.getFilterPropertyType() != null);
	}

	private String getObjectName() {
		String objectName = params.getItemKind().getSimpleName();
		if (objectName == null) {
			logger.debug(String.format("Could not get a class name for the item kind %s. Is it registered?", params.getItemKind()));
			return null;
		}
		return objectName;
	}

}
